home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 1.toast / pc / sample code / contributed / waste / waste 1.3 / source / wehighlevelediting.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  34.9 KB  |  1,606 lines

  1. /*
  2.  *    WEHighLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  High-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1998 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal void _WEPushAction(WEActionHandle hAction)
  18. {
  19.     WEPtr pWE = *((*hAction)->hOwner);
  20.     WEActionHandle hLast;
  21.  
  22.     // find the last action in the given stack
  23.     for ( hLast = hAction; (*hLast)->hNext != nil; hLast = (*hLast)->hNext )
  24.         ;
  25.  
  26.     // prepend hAction in front of the action stack
  27.     (*hLast)->hNext = pWE->hActionStack;
  28.     pWE->hActionStack = hAction;
  29. }
  30.  
  31. pascal OSErr _WENewAction(SInt32 rangeStart, SInt32 rangeEnd, SInt32 newTextLength,
  32.                             WEActionKind actionKind, WEActionFlags actionFlags,
  33.                             WEHandle hWE, WEActionHandle *hAction)
  34. {
  35.     WEAction * pAction = nil;
  36.     OSErr err;
  37.  
  38.     // allocate a new action record
  39.     if ((err = _WEAllocate(sizeof(WEAction), kAllocClear, (Handle *)hAction)) != noErr)
  40.     {
  41.         goto cleanup;
  42.     }
  43.  
  44.     // lock it down
  45.     HLock((Handle) *hAction);
  46.     pAction = **hAction;
  47.  
  48.     // fill in the fields
  49.     pAction->hOwner = hWE;
  50.     pAction->delRangeStart = rangeStart;
  51.     pAction->delRangeLength = newTextLength;
  52.     pAction->insRangeLength = rangeEnd - rangeStart;
  53.     pAction->actionKind = actionKind;
  54.     pAction->actionFlags = actionFlags;
  55.  
  56.     // remember selection range
  57.     WEGetSelection(&pAction->hiliteStart, &pAction->hiliteEnd, hWE);
  58.  
  59.     // allocate a handle to hold the text to be saved, unless otherwise specified
  60.     if ((actionFlags & weAFDontSaveText) == 0)
  61.     {
  62.         if ((err = _WEAllocate(0, kAllocTemp, &pAction->hText)) != noErr)
  63.         {
  64.             goto cleanup;
  65.         }
  66.     }
  67.  
  68.     // allocate a handle to hold the styles to be saved, unless otherwise specified
  69.     if ((actionFlags & weAFDontSaveStyles) == 0)
  70.     {
  71.         if ((err = _WEAllocate(0, kAllocTemp, &pAction->hStyles)) != noErr)
  72.         {
  73.             goto cleanup;
  74.         }
  75.     }
  76.  
  77. #if WASTE_OBJECTS
  78.     // allocate a handle to hold the "soup" to be saved, unless otherwise specified
  79.     if ((actionFlags & weAFDontSaveSoup) == 0)
  80.     {
  81.         if ((err = _WEAllocate(0, kAllocTemp, &pAction->hSoup)) != noErr)
  82.         {
  83.             goto cleanup;
  84.         }
  85.     }
  86. #endif
  87.  
  88.     // make a copy of text range
  89.     if ((err = WECopyRange(rangeStart, rangeEnd, pAction->hText, (Handle) pAction->hStyles, pAction->hSoup, hWE)) != noErr)
  90.     {
  91.         goto cleanup;
  92.     }
  93.  
  94.     // unlock action record
  95.     HUnlock((Handle) *hAction);
  96.  
  97. cleanup:
  98.     // clean up if an error occurred
  99.     if (err != noErr)
  100.     {
  101.         if (pAction != nil)
  102.         {
  103.             _WEForgetHandle(&pAction->hText);
  104.             _WEForgetHandle(&pAction->hStyles);
  105. #if WASTE_OBJECTS
  106.             _WEForgetHandle(&pAction->hSoup);
  107. #endif
  108.         }
  109.         _WEForgetHandle((Handle *)hAction);
  110.     }
  111.  
  112.     // return result code
  113.     return err;
  114. }
  115.  
  116. pascal void _WEDisposeAction(WEActionHandle hAction)
  117. {
  118.     WEAction *pAction;
  119.     WEActionHandle hNext;
  120.  
  121.     for ( ; hAction != nil; hAction = hNext )
  122.     {
  123.         // lock the action record
  124.         HLock((Handle) hAction);
  125.         pAction = *hAction;
  126.         hNext = pAction->hNext;
  127.  
  128.         // throw away text, styles and soup
  129.         _WEForgetHandle(&pAction->hText);
  130.         _WEForgetHandle(&pAction->hStyles);
  131. #if WASTE_OBJECTS
  132.         _WEForgetHandle(&pAction->hSoup);
  133. #endif
  134.  
  135.         // throw away the action record itself
  136.         DisposeHandle((Handle) hAction);
  137.     }
  138. }
  139.  
  140. pascal void _WEForgetAction(WEActionHandle *hAction)
  141. {
  142.     WEActionHandle theAction;
  143.  
  144.     theAction = *hAction;
  145.     if (theAction != nil)
  146.     {
  147.         *hAction = nil;
  148.         _WEDisposeAction(theAction);
  149.     }
  150. }
  151.  
  152. pascal OSErr _WEDoAction(WEActionHandle hAction)
  153. {
  154.     WEActionHandle hRedoAction;
  155.     WEAction *pAction = nil;
  156.     WEHandle hWE;
  157.     WEPtr pWE;
  158.     SInt32 offset, delOffset, insOffset;
  159.     SInt32 redrawStart, redrawEnd;
  160.     Boolean saveActionLock, saveWELock, saveTextLock;
  161.     OSErr err;
  162.  
  163.     // sanity check: make sure hAction isn't nil
  164.     if (hAction == nil)
  165.     {
  166.         return nilHandleErr;
  167.     }
  168.  
  169.     // get handle to associated WE instance
  170.     hWE = (*hAction)->hOwner;
  171.  
  172.     // lock the WE record
  173.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  174.     pWE = *hWE;
  175.  
  176.     // return an error code if this instance is read-only
  177.     err = weReadOnlyErr;
  178.     if (BTST(pWE->features, weFReadOnly))
  179.     {
  180.         goto cleanup;
  181.     }
  182.  
  183.     // stop any ongoing inline input session
  184.     WEStopInlineSession(hWE);
  185.  
  186.     // hide selection highlighting and the caret
  187.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  188.     if (BTST(pWE->flags, weFCaretVisible))
  189.     {
  190.         _WEBlinkCaret(hWE);
  191.     }
  192.  
  193.     redrawStart = LONG_MAX;
  194.     redrawEnd = 0;
  195.  
  196.     for ( ; hAction != nil; hAction = (*hAction)->hNext )
  197.     {
  198.         // lock the action record
  199.         saveActionLock = _WESetHandleLock((Handle) hAction, true);
  200.         pAction = *hAction;
  201.         offset = pAction->delRangeStart;
  202.         delOffset = offset + pAction->delRangeLength;
  203.         insOffset = offset + pAction->insRangeLength;
  204.  
  205.         // update the modification count of the affected WE instance
  206.         // note that undoing a change _decrements_ the modification count,
  207.         // while redoing the change increments it again
  208.         if (pAction->actionFlags & weAFIsRedo)
  209.         {
  210.             pWE->modCount++;
  211.         }
  212.         else
  213.         {
  214.             pWE->modCount--;
  215.         }
  216.  
  217.         // if undo support is enabled, save the range to be affected by this action
  218.         if (BTST(pWE->features, weFUndoSupport))
  219.         {
  220.             if (_WENewAction(offset, delOffset, pAction->insRangeLength, pAction->actionKind,
  221.                 (pAction->actionFlags ^ weAFIsRedo), hWE, &hRedoAction) == noErr)
  222.             {
  223.                 _WEPushAction(hRedoAction);
  224.             }
  225.         }
  226.  
  227.         if (pAction->hText != nil)
  228.         {
  229.             // delete the range to replace
  230.             if ((err = _WEDeleteRange(offset, delOffset, hWE)) != noErr)
  231.             {
  232.                 goto cleanup;
  233.             }
  234.  
  235.             // insert the saved text
  236.             saveTextLock = _WESetHandleLock(pAction->hText, true);
  237.             err = _WEInsertText(offset, *pAction->hText, pAction->insRangeLength, hWE);
  238.             _WESetHandleLock(pAction->hText, saveTextLock);
  239.             if (err != noErr)
  240.             {
  241.                 goto cleanup;
  242.             }
  243.         }
  244.  
  245.         // apply the saved styles, if any
  246.         if (pAction->hStyles != nil)
  247.         {
  248.             if ((err = _WEApplyStyleScrap(offset, insOffset, (StScrpHandle) pAction->hStyles, hWE)) != noErr)
  249.             {
  250.                 goto cleanup;
  251.             }
  252.         }
  253.  
  254. #if WASTE_OBJECTS
  255.         // the same goes for the soup
  256.         if (pAction->hSoup != nil)
  257.         {
  258.             if ((err = _WEApplySoup(offset, pAction->hSoup, hWE)) != noErr)
  259.             {
  260.                 goto cleanup;
  261.             }
  262.         }
  263. #endif
  264.  
  265.         // adjust redraw range (??? will this work with "complex" action stacks ???)
  266.         if (offset < redrawStart)
  267.         {
  268.             redrawStart = offset;
  269.         }
  270.         if (insOffset > redrawEnd)
  271.         {
  272.             redrawEnd = insOffset;
  273.         }
  274.  
  275.         // unlock action record
  276.         _WESetHandleLock((Handle) hAction, saveActionLock);
  277.  
  278.     } // for
  279.  
  280.     // restore the original selection range
  281.     pWE->selStart = pAction->hiliteStart;
  282.     pWE->selEnd = pAction->hiliteEnd;
  283.  
  284.     // redraw the text
  285.     if ((err = _WERedraw(redrawStart, redrawEnd, hWE)) != noErr)
  286.     {
  287.         goto cleanup;
  288.     }
  289.  
  290.     // clear result code
  291.     err = noErr;
  292.  
  293. cleanup:
  294.     // unlock the WE record
  295.     _WESetHandleLock((Handle) hWE, saveWELock);
  296.  
  297.     // return result code
  298.     return err;
  299. }
  300.  
  301. pascal OSErr WEUndo(WEHandle hWE)
  302. {
  303.     WEPtr pWE;
  304.     WEActionHandle hAction;
  305.     Boolean saveWELock;
  306.     OSErr err;
  307.  
  308.     // lock the WE record
  309.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  310.     pWE = *hWE;
  311.  
  312.     // "detach" the action stack from the WE instance
  313.     hAction = pWE->hActionStack;
  314.     pWE->hActionStack = nil;
  315.  
  316.     if (hAction != nil)
  317.     {
  318.         // perform the action...
  319.         err = _WEDoAction(hAction);
  320.  
  321.         // ...and throw it away
  322.         _WEDisposeAction(hAction);
  323.     }
  324.     else
  325.     {
  326.         // return an error code if the undo buffer is empty
  327.         err = weCantUndoErr;
  328.     }
  329.  
  330.     // unlock the WE record
  331.     _WESetHandleLock((Handle) hWE, saveWELock);
  332.  
  333.     return err;
  334. }
  335.  
  336. pascal void WEClearUndo(WEHandle hWE)
  337. {
  338.     WEPtr pWE = *hWE;
  339.  
  340.     // dispose of the action chain associated with the given WE instance,
  341.     // unless weFAccumulateUndos is set
  342.     if (! BTST(pWE->flags, weFAccumulateUndos))
  343.     {
  344.         _WEForgetAction(&pWE->hActionStack);
  345.     }
  346. }
  347.  
  348. pascal WEActionKind WEGetUndoInfo(Boolean *redoFlag, WEHandle hWE)
  349. {
  350.     WEActionHandle hAction;
  351.     WEActionKind theKind = weAKNone;
  352.     Boolean theFlag = false;
  353.  
  354.     if ((hAction = (*hWE)->hActionStack) != nil)
  355.     {
  356.         theKind = (*hAction)->actionKind;
  357.         theFlag = (((*hAction)->actionFlags & weAFIsRedo) != 0);
  358.     }
  359.  
  360.     if (redoFlag != nil)
  361.     {
  362.         *redoFlag = theFlag;
  363.     }
  364.     return theKind;
  365. }
  366.  
  367. pascal OSErr WEBeginAction(WEHandle hWE)
  368. {
  369.     WEPtr pWE = *hWE;
  370.  
  371.     if (BTST(pWE->flags, weFAccumulateUndos))
  372.     {
  373.         //    calling WEBeginAction twice in a row
  374.         //    is considered a protocol error
  375.         return weProtocolErr;
  376.     }
  377.  
  378.     //    set weFAccumulateUndos so that each new change will add to,
  379.     //    rather than replace, the existing action stack
  380.     BSET(pWE->flags, weFAccumulateUndos);
  381.  
  382.     //    start with a fresh action stack
  383.     _WEForgetAction(&pWE->hActionStack);
  384.  
  385.     return noErr;
  386. }
  387.  
  388. pascal OSErr WEEndAction(WEActionKind actionKind, WEHandle hWE)
  389. {
  390.     WEPtr pWE = *hWE;
  391.  
  392.     if (! BTST(pWE->flags, weFAccumulateUndos))
  393.     {
  394.         //    a call to WEEndAction not balanced by a previous
  395.         //    call to WEBeginAction is a protocol error
  396.         return weProtocolErr;
  397.     }
  398.  
  399.     //    stop accumulating actions
  400.     BCLR(pWE->flags, weFAccumulateUndos);
  401.  
  402.     //    make sure we have a non-empty action stack
  403.     if (pWE->hActionStack == nil)
  404.     {
  405.         return weCantUndoErr;
  406.     }
  407.  
  408.     //    set the action kind
  409.     (*pWE->hActionStack)->actionKind = actionKind;
  410.  
  411.     return noErr;
  412. }
  413.  
  414. pascal UInt32 WEGetModCount(WEHandle hWE)
  415. {
  416.     return (*hWE)->modCount;
  417. }
  418.  
  419. pascal void WEResetModCount(WEHandle hWE)
  420. {
  421.     (*hWE)->modCount = 0;
  422.     _WEForgetAction(&(*hWE)->hActionStack);
  423. }
  424.  
  425. pascal void _WEAdjustUndoRange(SInt32 moreBytes, WEHandle hWE)
  426. {
  427.     WEActionHandle hAction;
  428.  
  429.     if ((hAction = (*hWE)->hActionStack) != nil)
  430.     {
  431.         (*hAction)->delRangeLength += moreBytes;
  432.     }
  433. }
  434.  
  435. pascal OSErr _WETypeChar(char theByte, WEHandle hWE)
  436. {
  437.     WEPtr pWE;
  438.     char db[2];
  439.     SInt32 offset, endOffset, charLength;
  440.     OSErr err;
  441.  
  442.     pWE = *hWE;                    // the WE record must be already locked
  443.     charLength = 1;                // assume 1-byte character by default
  444.     db[0] = theByte;
  445.     offset = pWE->selStart;
  446.  
  447.     // delete current selection, if any
  448.     if ((err = _WEDeleteRange(offset, pWE->selEnd, hWE)) != noErr)
  449.     {
  450.         goto cleanup;
  451.     }
  452.  
  453.     pWE->selEnd = offset; // needed in case we take a premature exit
  454.  
  455.     // make sure the font script is synchronized with the keyboard script
  456.     _WESynchNullStyle(hWE);
  457.  
  458.     if (BTST(pWE->flags, weFDoubleByte))
  459.     {
  460.  
  461.         // special processing for double-byte characters
  462.         if (pWE->firstByte != 0)
  463.         {
  464.  
  465.             // if this byte is the second half of a double-byte character,
  466.             // insert the two bytes at the same time (flush the double-byte cache)
  467.             db[0] = pWE->firstByte;
  468.             db[1] = theByte;
  469.             charLength = 2;
  470.             pWE->firstByte = 0;
  471.         }
  472.         else
  473.         {
  474.  
  475.             // if theByte is the first half of a double-byte character, just cache it and exit
  476.             if (CallWECharByteProc(&theByte, 0, FontToScript(pWE->nullStyle.runStyle.tsFont),
  477.                 hWE, pWE->charByteHook) == smFirstByte)
  478.             {
  479.                 pWE->firstByte = theByte;
  480.                 return noErr;
  481.             }
  482.         }
  483.  
  484.     } // if double-byte script installed
  485.  
  486.     // insert the new character into the text
  487.     if ((err = _WEInsertText(offset, db, charLength, hWE)) != noErr)
  488.     {
  489.         goto cleanup;
  490.     }
  491.  
  492.     // adjust undo buffer for the new character
  493.     _WEAdjustUndoRange(charLength, hWE);
  494.  
  495.     // invalid the null style
  496.     BCLR(pWE->flags, weFUseNullStyle);
  497.  
  498.     // move the insertion point after the new character
  499.     endOffset = offset + charLength;
  500.     pWE->selStart = endOffset;
  501.     pWE->selEnd = endOffset;
  502.  
  503.     // redraw the text
  504.     if ((err = _WERedraw(offset, endOffset, hWE)) != noErr)
  505.     {
  506.         goto cleanup;
  507.     }
  508.  
  509.     // clear result code
  510.     err = noErr;
  511.  
  512. cleanup:
  513.     // return result code
  514.     return err;
  515.  
  516. }
  517.  
  518. pascal OSErr _WEBackspace(WEHandle hWE)
  519. {
  520.     // this routine is called by WEKey to handle the backspace key
  521.  
  522.     WEPtr pWE = *hWE;    // assume WE record is already locked
  523.     WEAction *pAction;
  524.     SInt32 rangeStart, rangeEnd, charLength;
  525.     WERunInfo runInfo;
  526.     char db[2];
  527.     Boolean saveActionLock;
  528.     OSErr err;
  529.  
  530.     // calculate the text range to delete
  531.     // if the selection is non-empty, delete that
  532.     rangeStart = pWE->selStart;
  533.     rangeEnd = pWE->selEnd;
  534.     if (rangeStart == rangeEnd)
  535.     {
  536.  
  537.         // otherwise the selection is an insertion point
  538.         // do nothing if insertion point is at the beginning of the text
  539.         if (rangeStart == 0)
  540.         {
  541.             return noErr;
  542.         }
  543.  
  544.         // determine the byte-type of the character preceding the insertion point
  545.         charLength = (WECharByte(rangeStart - 1, hWE) == smSingleByte) ? 1 : 2;
  546.         rangeStart -= charLength;
  547.  
  548.         if (pWE->hActionStack != nil)
  549.         {
  550.             // UNDO SUPPORT FOR BACKSPACES
  551.  
  552.             // lock the action record
  553.             saveActionLock = _WESetHandleLock((Handle) pWE->hActionStack, true);
  554.             pAction = *pWE->hActionStack;
  555.  
  556.             // backspaces over the newly entered text aren't a problem
  557.             if (pAction->delRangeLength > 0)
  558.             {
  559.                 pAction->delRangeLength -= charLength;
  560.             }
  561.             else
  562.             {
  563.  
  564.                 // the hard part comes when backspacing past the new text because
  565.                 // the user is about to delete a character not included in the block we saved
  566.                 db[0] = WEGetChar(rangeStart, hWE);
  567.                 if (charLength == 2)
  568.                 {
  569.                     db[1] = WEGetChar(rangeStart + 1, hWE);
  570.                 }
  571.  
  572.                 // prepend the character to be deleted to the beginning of our saved text handle
  573.                 if ((err = _WESplice(pAction->hText, &db, charLength, 0)) != noErr)
  574.                 {
  575.                     return err;
  576.                 }
  577.  
  578.                 // adjust internal counters
  579.                 pAction->insRangeLength += charLength;
  580.                 pAction->delRangeStart -= charLength;
  581.  
  582.                 // get style run info associated with the about-to-be-deleted character
  583.                 WEGetRunInfo(rangeStart, &runInfo, hWE);
  584.  
  585.                 // prepend a new style element to our style scrap, if necessary
  586.                 if ((err = _WEPrependStyle(pAction->hStyles, &runInfo, charLength)) != noErr)
  587.                 {
  588.                     return err;
  589.                 }
  590.  
  591. #if WASTE_OBJECTS
  592.                 // do the same with our object "soup"
  593.                 if ((err = _WEPrependObject(pAction->hSoup, &runInfo, charLength)) != noErr)
  594.                 {
  595.                     return err;
  596.                 }
  597. #endif
  598.  
  599.             } // if deleting old text
  600.  
  601.             // unlock the action record
  602.             _WESetHandleLock((Handle) pWE->hActionStack, saveActionLock);
  603.  
  604.         } // if undo support is enabled
  605.     } // if selection is empty
  606.  
  607.     if ((err = _WEDeleteRange(rangeStart, rangeEnd, hWE)) != noErr)
  608.     {
  609.         return err;
  610.     }
  611.  
  612.     // keep track of current selection range
  613.     pWE->selStart = rangeStart;
  614.     pWE->selEnd = rangeStart;
  615.  
  616.     // redraw the text
  617.     err = _WERedraw(rangeStart, rangeStart, hWE);
  618.  
  619.     return err;
  620. }
  621.  
  622. pascal OSErr _WEForwardDelete(WEHandle hWE)
  623. {
  624.  
  625.     // this routine is called by WEKey to handle the forward delete key
  626.  
  627.     WEPtr pWE = *hWE;    // assume WE record is already locked
  628.     WEAction *pAction;
  629.     SInt32 rangeStart, rangeEnd, charLength;
  630.     WERunInfo runInfo;
  631.     char db[2];
  632.     Boolean saveActionLock;
  633.     OSErr err;
  634.  
  635.     // calculate the text range to delete
  636.     // if the selection is non-empty, delete that
  637.     rangeStart = pWE->selStart;
  638.     rangeEnd = pWE->selEnd;
  639.     if (rangeStart == rangeEnd)
  640.     {
  641.  
  642.         // otherwise the selection is an insertion point
  643.         // do nothing if insertion point is at the end of the text
  644.         if (rangeStart == pWE->textLength)
  645.         {
  646.             return noErr;
  647.         }
  648.  
  649.         // determine the byte-type of the character following the insertion point
  650.         charLength = (WECharByte(rangeStart, hWE) == smSingleByte) ? 1 : 2;
  651.         rangeEnd = rangeStart + charLength;
  652.  
  653.         if (pWE->hActionStack != nil)
  654.         {
  655.  
  656.             // UNDO SUPPORT FOR FORWARD DELETE
  657.  
  658.             // lock the action record
  659.             saveActionLock = _WESetHandleLock((Handle) pWE->hActionStack, true);
  660.             pAction = *pWE->hActionStack;
  661.  
  662.             // make a copy of the character about to be deleted
  663.             db[0] = WEGetChar(rangeStart, hWE);
  664.             if (charLength == 2)
  665.             {
  666.                 db[1] = WEGetChar(rangeStart + 1, hWE);
  667.             }
  668.  
  669.             // append it to the end of our saved text handle
  670.             if ((err = _WESplice(pAction->hText, &db, charLength, -1)) != noErr)
  671.             {
  672.                 return err;
  673.             }
  674.  
  675.             // get style run info associated with the about-to-be-deleted character
  676.             WEGetRunInfo(rangeStart, &runInfo, hWE);
  677.  
  678.             // append a new style element to our style scrap, if necessary
  679.             if ((err = _WEAppendStyle(pAction->hStyles, &runInfo, pAction->insRangeLength)) != noErr)
  680.             {
  681.                 return err;
  682.             }
  683.  
  684. #if WASTE_OBJECTS
  685.             // do the same with our object soup
  686.             if ((err = _WEAppendObject(pAction->hSoup, &runInfo, pAction->insRangeLength)) != noErr)
  687.             {
  688.                 return err;
  689.             }
  690. #endif
  691.  
  692.             // adjust internal counters
  693.             pAction->insRangeLength += charLength;
  694.  
  695.             // unlock the action record
  696.             _WESetHandleLock((Handle) pWE->hActionStack, saveActionLock);
  697.  
  698.         } // if undo support is enabled
  699.     } // if selection is empty
  700.  
  701.     if ((err = _WEDeleteRange(rangeStart, rangeEnd, hWE)) != noErr)
  702.     {
  703.         return err;
  704.     }
  705.  
  706.     // keep track of current selection range
  707.     pWE->selStart = rangeStart;
  708.     pWE->selEnd = rangeStart;
  709.  
  710.     // redraw the text
  711.     err = _WERedraw(rangeStart, rangeStart, hWE);
  712.  
  713.     return err;
  714. }
  715.  
  716. pascal Boolean WEIsTyping(WEHandle hWE)
  717. {
  718.     // return true if we're tracking a typing sequence in the specified WE instance
  719.  
  720.     WEPtr pWE = *hWE;
  721.     WEAction *pAction;
  722.  
  723.     // there must be an undo buffer
  724.     if (pWE->hActionStack == nil)
  725.     {
  726.         return false;
  727.     }
  728.  
  729.     pAction = *pWE->hActionStack;
  730.  
  731.     // the action kind must be "typing" and the redo flag must be clear
  732.     if ((pAction->actionKind != weAKTyping) || ((pAction->actionFlags & weAFIsRedo) != 0))
  733.     {
  734.         return false;
  735.     }
  736.  
  737.     // finally, the selection range mustn't have moved since the last WEKey
  738.     if ((pWE->selStart != pWE->selEnd) ||
  739.         (pWE->selStart != pAction->delRangeStart + pAction->delRangeLength))
  740.     {
  741.         return false;
  742.     }
  743.  
  744.     return true;
  745. }
  746.  
  747. pascal void WEKey(SInt16 key, EventModifiers modifiers, WEHandle hWE)
  748. {
  749.     WEPtr pWE;
  750.     WEActionHandle hAction;
  751.     Boolean saveWELock;
  752.  
  753.     // lock the WE record
  754.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  755.     pWE = *hWE;
  756.  
  757.     // hide the caret if it's showing
  758.     if (BTST(pWE->flags, weFCaretVisible))
  759.     {
  760.         _WEBlinkCaret(hWE);
  761.     }
  762.  
  763.     // hide the cursor (it will show again as soon as it's moved)
  764.     ObscureCursor();
  765.  
  766.     // dispatch on key class (arrow keys, printable characters, backspace)
  767.     if ((key >= kArrowLeft) && (key <= kArrowDown))
  768.     {
  769.         _WEDoArrowKey(key, modifiers, hWE);
  770.     }
  771.     else
  772.     {
  773.  
  774.         // non-arrow keys modify the text, so make sure editing is allowed
  775.         if (!BTST(pWE->features, weFReadOnly))
  776.         {
  777.             // are we tracking a typing sequence?
  778.             if (!WEIsTyping(hWE))
  779.             {
  780.                 // nope;  start a new one
  781.                 // increment modification count
  782.                 pWE->modCount++;
  783.  
  784.                 // if undo support is enabled, create a new action to keep track of typing
  785.                 if (BTST(pWE->features, weFUndoSupport))
  786.                 {
  787.                     WEClearUndo(hWE);
  788.                     if (_WENewAction(pWE->selStart, pWE->selEnd, 0, weAKTyping, 0, hWE, &hAction) == noErr)
  789.                     {
  790.                         _WEPushAction(hAction);
  791.                     }
  792.                 }
  793.             } // if WEIsTyping
  794.  
  795.             if (key == kBackspace)
  796.             {
  797.                 _WEBackspace(hWE);
  798.             }
  799.             else if (key == kForwardDelete)
  800.             {
  801.                 _WEForwardDelete(hWE);
  802.             }
  803.             else
  804.             {
  805.                 _WETypeChar(key, hWE);
  806.             }
  807.         } // if not read-only
  808.     }
  809.  
  810.     // unlock the WE record
  811.     _WESetHandleLock((Handle) hWE, saveWELock);
  812. }
  813.  
  814. pascal OSErr WEInsert(Ptr textPtr, SInt32 textLength, StScrpHandle hStyles, Handle hSoup, WEHandle hWE)
  815. {
  816. #if !WASTE_OBJECTS
  817. #pragma unused(hSoup)
  818. #endif
  819.     WEPtr pWE;
  820.     SInt32 offset, endOffset;
  821.     WEActionHandle hAction;
  822.     SInt16 intPasteAction;
  823.     Boolean saveWELock;
  824.     char space = kSpace;
  825.     OSErr err;
  826.  
  827.     // lock the WE record
  828.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  829.     pWE = *hWE;
  830.     offset = pWE->selStart;
  831.  
  832.     // return an error code if this instance is read-only
  833.     err = weReadOnlyErr;
  834.     if (BTST(pWE->features, weFReadOnly))
  835.     {
  836.         goto cleanup;
  837.     }
  838.  
  839.     // stop any ongoing inline input session
  840.     WEStopInlineSession(hWE);
  841.  
  842.     // increment modification count
  843.     pWE->modCount++;
  844.  
  845.     // if undo support is enabled, save current selection range
  846.     if (BTST(pWE->features, weFUndoSupport))
  847.     {
  848.         WEClearUndo(hWE);
  849.         if (_WENewAction(offset, pWE->selEnd, textLength, weAKUnspecified, 0, hWE, &hAction) == noErr)
  850.         {
  851.             _WEPushAction(hAction);
  852.         }
  853.     }
  854.  
  855.     // delete current selection
  856.     if ((err = _WEDeleteRange(offset, pWE->selEnd, hWE)) != noErr)
  857.     {
  858.         goto cleanup;
  859.     }
  860.  
  861.     // insert the new text at the insertion point
  862.     if ((err = _WEInsertText(offset, textPtr, textLength, hWE)) != noErr)
  863.     {
  864.         goto cleanup;
  865.     }
  866.     endOffset = offset + textLength;
  867.  
  868.     if (hStyles != nil)
  869.     {
  870.  
  871.         // if a style scrap was supplied, apply it to the newly inserted text
  872.         if ((err = _WEApplyStyleScrap(offset, endOffset, hStyles, hWE)) != noErr)
  873.         {
  874.             goto cleanup;
  875.         }
  876.     }
  877.  
  878. #if WASTE_OBJECTS
  879.     if (hSoup != nil)
  880.     {
  881.         // if an object soup was supplied, apply it to the newly inserted text
  882.         if ((err = _WEApplySoup(offset, hSoup, hWE)) != noErr)
  883.         {
  884.             goto cleanup;
  885.         }
  886.     }
  887. #endif
  888.  
  889.     // determine whether an extra space should be added before or after the inserted text
  890.     intPasteAction = _WEIntelligentPaste(offset, endOffset, hWE);
  891.  
  892.     // add the extra space, if necessary
  893.     if (intPasteAction != weDontAddSpaces)
  894.     {
  895.         if (intPasteAction == weAddSpaceOnLeftSide)
  896.         {
  897.             err = _WEInsertText(offset, &space, sizeof(space), hWE);
  898.         }
  899.         else
  900.         {
  901.             err = _WEInsertText(endOffset, &space, sizeof(space), hWE);
  902.         }
  903.         if (err != noErr)
  904.         {
  905.             goto cleanup;
  906.         }
  907.         endOffset++;
  908.  
  909.         // adjust undo buffer (if any) for the extra space
  910.         _WEAdjustUndoRange(sizeof(space), hWE);
  911.     }
  912.  
  913.     // invalid the null style
  914.     BCLR(pWE->flags, weFUseNullStyle);
  915.  
  916.     // move the insertion point at the end of the inserted text
  917.     pWE->selStart = endOffset;
  918.     pWE->selEnd = endOffset;
  919.  
  920.     // redraw the text
  921.     if ((err = _WERedraw(offset, endOffset, hWE)) != noErr)
  922.     {
  923.         goto cleanup;
  924.     }
  925.  
  926.     // clear result code
  927.     err = noErr;
  928.  
  929. cleanup:
  930.     // unlock the WE record
  931.     _WESetHandleLock((Handle) hWE, saveWELock);
  932.  
  933.     // return result code
  934.     return err;
  935. }
  936.  
  937. #if WASTE_OBJECTS
  938.  
  939. pascal OSErr WEInsertObject(FlavorType objectType, Handle objectDataHandle, Point objectSize, WEHandle hWE)
  940. {
  941.     WEPtr pWE;
  942.     WEActionHandle hAction;
  943.     SInt32 offset, endOffset;
  944.     WETextStyle ts;
  945.     Boolean saveWELock;
  946.     char marker = kObjectMarker;
  947.     OSErr err;
  948.  
  949.     BLOCK_CLR(ts);
  950.  
  951.     // lock the WE record
  952.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  953.     pWE = *hWE;
  954.     offset = pWE->selStart;
  955.  
  956.     // return an error code if this instance is read-only
  957.     err = weReadOnlyErr;
  958.     if (BTST(pWE->features, weFReadOnly))
  959.     {
  960.         goto cleanup;
  961.     }
  962.  
  963.     // stop any ongoing inline input session
  964.     WEStopInlineSession(hWE);
  965.  
  966.     // call the 'new' handler to initialize private object storage (if any)
  967.     // and to calculate the default size for this object
  968.  
  969.     if ((err = _WENewObject(objectType, objectDataHandle, hWE, &ts.tsObject)) != noErr)
  970.     {
  971.         goto cleanup;
  972.     }
  973.  
  974.     // use the specified object size, unless it is (0, 0), in which case keep the default size
  975.     if (* (SInt32 *) &objectSize != 0)
  976.     {
  977.         (*ts.tsObject)->objectSize = objectSize;
  978.     }
  979.  
  980.     // increment modification count
  981.     pWE->modCount++;
  982.  
  983.     // if undo support is enabled, save current selection range
  984.     if (BTST(pWE->features, weFUndoSupport))
  985.     {
  986.         WEClearUndo(hWE);
  987.         if (_WENewAction(offset, pWE->selEnd, 1, weAKUnspecified, 0, hWE, &hAction) == noErr)
  988.         {
  989.             _WEPushAction(hAction);
  990.         }
  991.     }
  992.  
  993.     // delete current selection
  994.     if ((err = _WEDeleteRange(offset, pWE->selEnd, hWE)) != noErr)
  995.     {
  996.         goto cleanup;
  997.     }
  998.  
  999.     // insert a kObjectMarker character at the insertion point
  1000.     if ((err = _WEInsertText(offset, &marker, sizeof(marker), hWE)) != noErr)
  1001.     {
  1002.         goto cleanup;
  1003.     }
  1004.  
  1005.     // move the insertion point after the inserted text
  1006.     endOffset = offset + 1;
  1007.     pWE->selStart = endOffset;
  1008.     pWE->selEnd = endOffset;
  1009.  
  1010.     // record a reference to the object descriptor in the style table
  1011.     err = _WESetStyleRange(offset, endOffset, weDoObject, &ts, hWE);
  1012.     ts.tsObject = nil;
  1013.     if (err != noErr)
  1014.     {
  1015.         goto cleanup;
  1016.     }
  1017.  
  1018.     // invalid the null style
  1019.     BCLR(pWE->flags, weFUseNullStyle);
  1020.  
  1021.     // redraw the text
  1022.     if ((err = _WERedraw(offset, endOffset, hWE)) != noErr)
  1023.     {
  1024.         goto cleanup;
  1025.     }
  1026.  
  1027.     // clear result code
  1028.     err = noErr;
  1029.  
  1030. cleanup:
  1031.     // clean up
  1032.     _WEForgetHandle((Handle *) &ts.tsObject);
  1033.  
  1034.     // unlock the WE record
  1035.     _WESetHandleLock((Handle) hWE, saveWELock);
  1036.  
  1037.     // return result code
  1038.     return err;
  1039. }
  1040.  
  1041. #endif
  1042.  
  1043. pascal OSErr WEDelete(WEHandle hWE)
  1044. {
  1045.     WEPtr pWE;
  1046.     WEActionHandle hAction;
  1047.     SInt32 rangeStart, rangeEnd;
  1048.     Boolean saveWELock;
  1049.     OSErr err;
  1050.  
  1051.     // lock the WE record
  1052.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1053.     pWE = *hWE;
  1054.  
  1055.     // return an error code if this instance is read-only
  1056.     err = weReadOnlyErr;
  1057.     if (BTST(pWE->features, weFReadOnly))
  1058.     {
  1059.         goto cleanup;
  1060.     }
  1061.  
  1062.     // stop any ongoing inline input session
  1063.     WEStopInlineSession(hWE);
  1064.  
  1065.     // get current selection range
  1066.     rangeStart = pWE->selStart;
  1067.     rangeEnd = pWE->selEnd;
  1068.  
  1069.     // do nothing if the selection range is empty
  1070.     if (rangeStart < rangeEnd)
  1071.     {
  1072.  
  1073.         // increment modification count
  1074.         pWE->modCount++;
  1075.  
  1076.         // range extension for intelligent cut-and-paste
  1077.         _WEIntelligentCut(&rangeStart, &rangeEnd, hWE);
  1078.  
  1079.         // if undo support is enabled, save the range to be deleted
  1080.         if (BTST(pWE->features, weFUndoSupport))
  1081.         {
  1082.             WEClearUndo(hWE);
  1083.             if (_WENewAction(rangeStart, rangeEnd, 0, weAKClear, 0, hWE, &hAction) == noErr)
  1084.             {
  1085.                 _WEPushAction(hAction);
  1086.             }
  1087.         }
  1088.  
  1089.         // delete the selection range
  1090.         if ((err = _WEDeleteRange(rangeStart, rangeEnd, hWE)) != noErr)
  1091.         {
  1092.             goto cleanup;
  1093.         }
  1094.  
  1095.         // reset the selection range
  1096.         pWE->selStart = rangeStart;
  1097.         pWE->selEnd = rangeStart;
  1098.  
  1099.         // redraw the text
  1100.         if ((err = _WERedraw(rangeStart, rangeStart, hWE)) != noErr)
  1101.         {
  1102.             goto cleanup;
  1103.         }
  1104.     } // if non-empty selection
  1105.  
  1106.     // clear result code
  1107.     err = noErr;
  1108.  
  1109. cleanup:
  1110.     // unlock the WE record
  1111.     _WESetHandleLock((Handle) hWE, saveWELock);
  1112.  
  1113.     // return result code
  1114.     return err;
  1115. }
  1116.  
  1117. pascal OSErr WECut(WEHandle hWE)
  1118. {
  1119.     OSErr err;
  1120.  
  1121.     // first copy...
  1122.     if ((err = WECopy(hWE)) != noErr)
  1123.     {
  1124.         goto cleanup;
  1125.     }
  1126.  
  1127.     // ... then delete
  1128.     if ((err = WEDelete(hWE)) != noErr)
  1129.     {
  1130.         goto cleanup;
  1131.     }
  1132.  
  1133.     // change the action kind of the most recent action, if any
  1134.     if ((*hWE)->hActionStack != nil)
  1135.     {
  1136.         (*(*hWE)->hActionStack)->actionKind = weAKCut;
  1137.     }
  1138.  
  1139. cleanup:
  1140.     // return result code
  1141.     return err;
  1142. }
  1143.  
  1144. pascal Boolean WECanPaste(WEHandle hWE)
  1145. {
  1146.     SInt32 scrapOffset;
  1147. #if WASTE_OBJECTS
  1148.     FlavorType objectType;
  1149.     SInt32 index;
  1150. #endif
  1151.  
  1152.     if (!BTST((*hWE)->features, weFReadOnly))
  1153.     {
  1154.         // return true if the desk scrap contains a text flavor
  1155.         if (GetScrap(nil, kTypeText, &scrapOffset) > 0)
  1156.         {
  1157.             return true;
  1158.         }
  1159.  
  1160. #if WASTE_OBJECTS
  1161.         // see if the desk scrap contains a flavor matching one of the registered object types
  1162.         index = 0;
  1163.         while (_WEGetIndObjectType(index, &objectType, hWE) == noErr)
  1164.         {
  1165.             if (GetScrap(nil, objectType, &scrapOffset) > 0)
  1166.             {
  1167.                 return true;
  1168.             }
  1169.             index++;
  1170.         } // while
  1171. #endif
  1172.     } // if not read-only
  1173.     return false;
  1174. }
  1175.  
  1176. pascal OSErr _WEGetScrapHandle ( FlavorType dataFlavor, Handle * dataHandle )
  1177. {
  1178.     SInt32 scrapOffset ;
  1179.     SInt32 scrapResult ;
  1180.     OSErr err ;
  1181.  
  1182.     * dataHandle = nil;
  1183.  
  1184.     //    allocate a temporary handle
  1185.     if ( ( err = _WEAllocate ( 0, kAllocTemp, dataHandle ) ) != noErr )
  1186.     {
  1187.         goto cleanup ;
  1188.     }
  1189.  
  1190.     //    look for an item with the specified flavor on the desk scrap
  1191.     if ( ( scrapResult = GetScrap ( * dataHandle, dataFlavor, & scrapOffset ) ) < 0 )
  1192.     {
  1193.         //    a negative result from GetScrap is an error code
  1194.         err = scrapResult ;
  1195.         _WEForgetHandle ( dataHandle ) ;
  1196.         goto cleanup ;
  1197.     }
  1198.  
  1199.     //    clear result code
  1200.     err = noErr ;
  1201.  
  1202. cleanup :
  1203.     //    return result code
  1204.     return err ;
  1205. }
  1206.  
  1207. pascal OSErr WEPaste ( WEHandle hWE )
  1208. {
  1209.     Handle hText = nil ;
  1210.     Handle hStyles = nil ;
  1211.     Handle hFontTable = nil ;
  1212.     Handle hSoup = nil ;
  1213.     OSErr err;
  1214.  
  1215.     // look for a text flavor
  1216.     if ( ( err = _WEGetScrapHandle ( kTypeText, & hText ) ) == noTypeErr )
  1217.     {
  1218.  
  1219. #if WASTE_OBJECTS
  1220.         // no text: look for a flavor matching one of the registered object types
  1221.  
  1222.         Handle hObjectData = nil ;
  1223.         FlavorType objectType = 0 ;
  1224.         SInt32 index = 0 ;
  1225.  
  1226.         while ( _WEGetIndObjectType ( index, & objectType, hWE ) == noErr )
  1227.         {
  1228.             if ( ( err = _WEGetScrapHandle ( objectType, & hObjectData ) ) == noErr )
  1229.             {
  1230.                 Point objectSize ;
  1231.                 * ( SInt32 * ) & objectSize = 0 ;
  1232.  
  1233.                 // found a registered type: create a new object out of the tagged data
  1234.                 if ( ( err = WEInsertObject ( objectType, hObjectData, objectSize, hWE ) ) != noErr )
  1235.                 {
  1236.                     _WEForgetHandle ( & hObjectData ) ;
  1237.                 }
  1238.                 goto cleanup;
  1239.             }
  1240.             else if ( err != noTypeErr )
  1241.             {
  1242.                 goto cleanup ;
  1243.             }
  1244.  
  1245.             // try with next flavor
  1246.             index++;
  1247.         } // while
  1248. #endif
  1249.  
  1250.         // nothing pasteable: return error code
  1251.         goto cleanup;
  1252.     }
  1253.     else if ( err != noErr )
  1254.     {
  1255.         goto cleanup ;
  1256.     }
  1257.  
  1258.     if ( ! BTST ( ( * hWE ) -> features, weFMonoStyled ) )
  1259.     {
  1260.         //    look for a style scrap accompanying the text
  1261.         _WEGetScrapHandle ( kTypeStyles, & hStyles ) ;
  1262.  
  1263.         if ( hStyles != nil )
  1264.         {
  1265.             //    if a style scrap is available, further look for a supplemental font table
  1266.             if ( _WEGetScrapHandle ( kTypeFontTable, & hFontTable ) == noErr )
  1267.             {
  1268.                 Boolean wasChanged = false ;
  1269.  
  1270.                 //    perform any necessary font ID remapping
  1271.                 if ( ( WEUpdateFontTable ( hFontTable, nil, & wasChanged ) == noErr ) && wasChanged )
  1272.                 {
  1273.                     WEUpdateStyleScrap ( ( StScrpHandle ) hStyles, hFontTable ) ;
  1274.                 }
  1275.             }
  1276.         }
  1277.  
  1278. #if WASTE_OBJECTS
  1279.         //    look for an object soup
  1280.         _WEGetScrapHandle ( kTypeSoup, & hSoup ) ;
  1281. #endif
  1282.     } // if not mono-styled
  1283.  
  1284.     // lock down the text
  1285.     HLock ( hText ) ;
  1286.  
  1287.     // insert the text
  1288.     err = WEInsert ( * hText, GetHandleSize ( hText ), ( StScrpHandle ) hStyles, hSoup, hWE ) ;
  1289.  
  1290. cleanup :
  1291.     // if successful, change the action kind of the most recent action, if any
  1292.     if ( ( err == noErr ) && ( ( *hWE ) -> hActionStack != nil ) )
  1293.     {
  1294.         ( * ( * hWE ) -> hActionStack ) -> actionKind = weAKPaste ;
  1295.     }
  1296.  
  1297.     // clean up
  1298.     _WEForgetHandle ( & hText ) ;
  1299.     _WEForgetHandle ( & hStyles ) ;
  1300.     _WEForgetHandle ( & hFontTable ) ;
  1301. #if WASTE_OBJECTS
  1302.     _WEForgetHandle ( & hSoup ) ;
  1303. #endif
  1304.  
  1305.     // return result code
  1306.     return err ;
  1307. }
  1308.  
  1309. pascal OSErr _WESmartSetFont(WEStyleMode mode, const TextStyle *ts, WEHandle hWE)
  1310. {
  1311.     WEPtr pWE = *hWE;    // assume WE record is already locked
  1312.     ScriptCode script;
  1313.     SInt32 runIndex;
  1314.     SInt32 rangeStart, rangeEnd;
  1315.     WERunInfo runInfo;
  1316.     GrafPtr savePort;
  1317.     SInt16 saveFont;
  1318.     OSErr err;
  1319.  
  1320.     // set up the graphics port
  1321.     GetPort(&savePort);
  1322.     SetPort(pWE->port);
  1323.     saveFont = pWE->port->txFont;
  1324.  
  1325.     // get the script corresponding to the font we're applying
  1326.     script = FontToScript(ts->tsFont);
  1327.  
  1328.     // walk through the style runs encompassing the selection range
  1329.     runIndex = WEOffsetToRun(pWE->selStart, hWE);
  1330.     do
  1331.     {
  1332.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  1333.  
  1334.         if (pWE->selStart > runInfo.runStart)
  1335.         {
  1336.             rangeStart = pWE->selStart;
  1337.         }
  1338.         else
  1339.         {
  1340.             rangeStart = runInfo.runStart;
  1341.         }
  1342.  
  1343.         if (pWE->selEnd < runInfo.runEnd)
  1344.         {
  1345.             rangeEnd = pWE->selEnd;
  1346.         }
  1347.         else
  1348.         {
  1349.             rangeEnd = runInfo.runEnd;
  1350.         }
  1351.  
  1352.         // does this style run belong to the same script we're applying?
  1353.         if (FontToScript(runInfo.runAttrs.runStyle.tsFont) == script)
  1354.         {
  1355.             if ((err = _WESetStyleRange(rangeStart, rangeEnd, weDoFont, (WETextStyle *) ts, hWE)) != noErr)
  1356.             {
  1357.                 goto cleanup;
  1358.             }
  1359.             runIndex = WEOffsetToRun(runInfo.runEnd, hWE);
  1360.         }
  1361.         else if (mode & weDoExtractSubscript)
  1362.         {
  1363.             SInt32 runLength;
  1364.             SInt32 subrunLength;
  1365.             ScriptRunStatus runStatus;
  1366.  
  1367.             // FindScriptRun takes an implicit parameter through the txFont field of thePort
  1368.             TextFont(runInfo.runAttrs.runStyle.tsFont);
  1369.  
  1370.             runLength = rangeEnd - rangeStart;
  1371.             while (runLength > 0)
  1372.             {
  1373.                 // lock text handle
  1374.                 Boolean saveTextLock = _WESetHandleLock(pWE->hText, true);
  1375.  
  1376.                 // look for blocks of subscript text
  1377.                 runStatus = FindScriptRun(*pWE->hText + rangeStart, runLength, &subrunLength);
  1378.  
  1379.                 // unlock text handle
  1380.                 _WESetHandleLock(pWE->hText, saveTextLock);
  1381.  
  1382.                 if (runStatus.script == script)
  1383.                 {
  1384.                     // "extract" subscript text
  1385.                     if ((err = _WESetStyleRange(rangeStart, rangeStart + subrunLength, weDoFont, (WETextStyle *) ts, hWE)) != noErr)
  1386.                     {
  1387.                         goto cleanup;
  1388.                     }
  1389.                 }
  1390.                 rangeStart += subrunLength;
  1391.                 runLength -= subrunLength;
  1392.             }
  1393.             runIndex = WEOffsetToRun(runInfo.runEnd, hWE);
  1394.         }
  1395.         else
  1396.             runIndex++;
  1397.  
  1398.     } while (runInfo.runEnd < pWE->selEnd);
  1399.  
  1400.     // clear result code
  1401.     err = noErr;
  1402.  
  1403. cleanup:
  1404.     // restore the port
  1405.     TextFont(saveFont);
  1406.     SetPort(savePort);
  1407.  
  1408.     // return result code
  1409.     return err;
  1410. }
  1411.  
  1412. pascal OSErr WESetStyle(WEStyleMode mode, const TextStyle *ts, WEHandle hWE)
  1413. {
  1414.     WEPtr pWE;
  1415.     WEActionHandle hAction;
  1416.     ScriptCode fontScript;
  1417.     Boolean saveWELock;
  1418.     OSErr err;
  1419.  
  1420.     // lock the WE record
  1421.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1422.     pWE = *hWE;
  1423.  
  1424.     // return an error code if this instance is read-only
  1425.     err = weReadOnlyErr;
  1426.     if (BTST(pWE->features, weFReadOnly))
  1427.     {
  1428.         goto cleanup;
  1429.     }
  1430.  
  1431.     // stop any ongoing inline input session
  1432.     WEStopInlineSession(hWE);
  1433.  
  1434.     if (BTST(pWE->features, weFMonoStyled))
  1435.     {
  1436.         // MONOSTYLED TEXT
  1437.         // apply the change to the whole text, not just to the selection range
  1438.         if ((err = _WESetStyleRange(0, pWE->textLength, mode, (WETextStyle *) ts, hWE)) != noErr)
  1439.         {
  1440.             goto cleanup;
  1441.         }
  1442.  
  1443.         // invalidate the null style record
  1444.         BCLR(pWE->flags, weFUseNullStyle);
  1445.  
  1446.         // redraw the text
  1447.         if ((err = _WERedraw(0, pWE->textLength, hWE)) != noErr)
  1448.         {
  1449.             goto cleanup;
  1450.         }
  1451.     }
  1452.     else if (pWE->selStart == pWE->selEnd)
  1453.     {
  1454.         // MULTISTYLED TEXT; NULL SELECTION
  1455.         // first make sure the nullStyle field contains valid information
  1456.         _WESynchNullStyle(hWE);
  1457.  
  1458.         // apply style changes to the nullStyle record
  1459.         _WECopyStyle((WETextStyle *) ts, &pWE->nullStyle.runStyle, pWE->nullStyle.runStyle.tsFace, mode);
  1460.  
  1461.         // special case: if this instance is empty, propagate the
  1462.         // change to the style table (this avoids some subtle problems)
  1463.         if (pWE->textLength == 0)
  1464.         {
  1465.             if ((err = _WESetStyleRange(0, 0, weDoAll + weDoReplaceFace, &pWE->nullStyle.runStyle, hWE)) != noErr)
  1466.             {
  1467.                 goto cleanup;
  1468.             }
  1469.         }
  1470.  
  1471. #if !WASTE_NO_SYNCH
  1472.         // if the font was altered, synchronize the keyboard script
  1473.         if (BTST(pWE->flags, weFNonRoman) && (mode & weDoFont))
  1474.         {
  1475.             fontScript = FontToScript(pWE->nullStyle.runStyle.tsFont);
  1476.             if (fontScript != GetScriptManagerVariable(smKeyScript))
  1477.             {
  1478.                 KeyScript(fontScript);
  1479.             }
  1480.         }
  1481. #endif
  1482.     }
  1483.     else
  1484.     {
  1485.         // MULTISTYLED TEXT; NON-EMPTY SELECTION
  1486.  
  1487.         // increment modification count
  1488.         pWE->modCount++;
  1489.  
  1490.         // if undo support is enabled, save the styles of the text range to be affected
  1491.         if (BTST(pWE->features, weFUndoSupport))
  1492.         {
  1493.             WEClearUndo(hWE);
  1494.             if (_WENewAction(pWE->selStart, pWE->selEnd, pWE->selEnd - pWE->selStart, weAKSetStyle,
  1495.                 weAFDontSaveText + weAFDontSaveSoup, hWE, &hAction) == noErr)
  1496.             {
  1497.                 _WEPushAction(hAction);
  1498.             }
  1499.         }
  1500.  
  1501.         // check for "smart" font modes
  1502.         if (BTST(pWE->flags, weFNonRoman) && ((mode & weDoSmartFont) == weDoSmartFont))
  1503.         {
  1504.             if ((err = _WESmartSetFont(mode, ts, hWE)) != noErr)
  1505.             {
  1506.                 goto cleanup;
  1507.             }
  1508.             mode &= ~weDoFont;
  1509.         }
  1510.  
  1511.         // set the style of the selection range
  1512.         if ((err = _WESetStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *) ts, hWE)) != noErr)
  1513.         {
  1514.             goto cleanup;
  1515.         }
  1516.  
  1517.         // and redraw the text
  1518.         if ((err = _WERedraw(pWE->selStart, pWE->selEnd, hWE)) != noErr)
  1519.         {
  1520.             goto cleanup;
  1521.         }
  1522.     }
  1523.  
  1524.     // clear the result code
  1525.     err = noErr;
  1526.  
  1527. cleanup:
  1528.     // unlock the WE record
  1529.     _WESetHandleLock((Handle) hWE, saveWELock);
  1530.  
  1531.     // return result code
  1532.     return err;
  1533. }
  1534.  
  1535. pascal OSErr WEUseStyleScrap(StScrpHandle hStyles, WEHandle hWE)
  1536. {
  1537.     WEPtr pWE;
  1538.     Boolean saveWELock;
  1539.     OSErr err;
  1540.  
  1541.     // lock the WE record
  1542.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1543.     pWE = *hWE;
  1544.  
  1545.     // return an error code if this instance is read-only
  1546.     err = weReadOnlyErr;
  1547.     if (BTST(pWE->features, weFReadOnly))
  1548.     {
  1549.         goto cleanup;
  1550.     }
  1551.  
  1552.     // apply the style scrap to the selection range
  1553.     if ((err = _WEApplyStyleScrap(pWE->selStart, pWE->selEnd, hStyles, hWE)) != noErr)
  1554.     {
  1555.         goto cleanup;
  1556.     }
  1557.  
  1558.     // redraw the text
  1559.     err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1560.  
  1561. cleanup:
  1562.     // unlock the WE record
  1563.     _WESetHandleLock((Handle) hWE, saveWELock);
  1564.  
  1565.     // return result code
  1566.     return err;
  1567. }
  1568.  
  1569. #if WASTE_OBJECTS
  1570.  
  1571. pascal OSErr WEUseSoup(Handle hSoup, WEHandle hWE)
  1572. {
  1573.     WEPtr pWE;
  1574.     Boolean saveWELock;
  1575.     OSErr err;
  1576.  
  1577.     // lock the WE record
  1578.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1579.     pWE = *hWE;
  1580.  
  1581.     // return an error code if this instance is read-only
  1582.     err = weReadOnlyErr;
  1583.     if (BTST(pWE->features, weFReadOnly))
  1584.     {
  1585.         goto cleanup;
  1586.     }
  1587.  
  1588.     // apply the soup starting from selStart
  1589.     if ((err = _WEApplySoup(pWE->selStart, hSoup, hWE)) != noErr)
  1590.     {
  1591.         goto cleanup;
  1592.     }
  1593.  
  1594.     // redraw the text
  1595.     err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1596.  
  1597. cleanup:
  1598.     // unlock the WE record
  1599.     _WESetHandleLock((Handle) hWE, saveWELock);
  1600.  
  1601.     // return result code
  1602.     return err;
  1603. }
  1604.  
  1605. #endif  // WASTE_OBJECTS
  1606.